/*
 * 程序名：crtsurfdata.cpp 本程序用于生成气象站点观测的分钟数据
 */
#include "_public.h"
using namespace idc;

// 省 站号 站名 纬度 经度 海拔高度
// 安徽,58015,砀山,34.27,116.2,44.2
struct st_stcode    // 站点参数的结构体
{
    char provname[31];  // 省   string 操作方便,自动扩容,但操作数据库时,string没有优势
    char obtid[11];     // 站号
    char obtname[31];   // 站名，同城市名
    double lat;         // 纬度：度
    double lon;         // 经度：度
    double height;      // 海拔高度：米
};
list<struct st_stcode> stlist;  // 存放全部的站点参数
bool loadstcode(const string &inifile); // 把站点参数文件加载到stlist容器中

// 气象站观测数据的结构体
struct st_surfdata
{
    char obtid[11];     // 站点号
    char ddatetime[15]; // 数据时间：格式yyyymmddhh24miss，精确到分钟，秒固定填00
    int t;              // 气温：单位，摄氏度
    int p;              // 气压：单位，百帕
    int u;              // 相对湿度：0-100之间的值
    int wd;             // 风向：0-360之间的值
    int wf;             // 风速：单位，m/s
    int r;              // 降雨量：单位，mm
    int vis;            // 能见度，单位，m
};
list<st_surfdata> datalist; // 存放观测数据的容器
void crtsurfdata();         // 根据stlist容器中的站点参数，生成站点观测数据，存放在datalist中

char strddatetime[15];

// 把容器datalist中的气象观测数据写入文件，outpath-数据文件存放的目录；datafmt-数据文件的格式，取值：csv、xml和json
bool crtsurffile(const string& outpath,const string& datafmt);

clogfile logfile;   // 本程序运行的日志

void EXIT(int sig); // 程序退出和信号2、15的处理函数。

cpactive pactive;   // 进程的心跳

int main(int argc, char *argv[])
{
    // 站点参数文件 生成的测试数据存放的目录
    if(argc!=5){
        // 如果参数非法，给出帮助文档
        cout<<"Using:./crtsurfdata inifile outpath logfile\n";
        cout<<"Examples:/project/tools/bin/procctl 60 /project/idc/bin/crtsurfdata /project/idc/ini/stcode.ini /tmp/idc/surfdata /log/idc/crtsurfdata.log csv,xml,json\n\n";
        
        cout<<"本程序用于生成气象站点的观测数据，程序每分钟运行一次，由调度模块启动。\n";
        cout<<"inifile 气象站点参数文件名。\n";
        cout<<"outpath 气象站点数据文件存放的目录。\n";
        cout<<"logfile 本程序运行的日志文件名。\n";
        cout<<"datafmt 输出数据文件的格式，支持csv、xml和json，中间用逗号分隔。\n";
        return -1;
    }
    
    // 设置信号，在shell状态下可用“kill + 进程号”正常终止些进程
    // 但请不要用“kill -9 +进程号”强行终止
    closeioandsignal(true); // 关闭0、1、2和忽略全部的信号
    signal(SIGINT,EXIT);
    signal(SIGTERM,EXIT);

    // 把当前进程的心跳加入共享内存。
    pactive.addpinfo(10,"crtsurfdata");
    
    // 打开日志文件
    if(logfile.open(argv[3])==false){
        cout<<"logfile.open()"<<argv[3]<<") failed.\n";
        return -1;
    }

    logfile.write("crtsurfdata 开始运行。\n");

    // 在这里编写处理业务的代码
    // 1) 从站点参数文件中加载站点参数，存放于容器中;
    if(loadstcode(argv[1])==false) EXIT(-1);
    // // 测试：文件中的数据是否存入容器：将容器中的数据写入日志
    // for(auto it = stlist.begin();it!=stlist.end();it++){
    //     logfile.write("%s\t%s\t%s\t%lf\t%lf\t%lf\n",(*it).provname,(*it).obtid,(*it).obtname,(*it).lat,(*it).lon,(*it).height);
    // }
    
    // 获取观测数据的时间
    memset(strddatetime,0,sizeof(strddatetime));
    ltime(strddatetime,"yyyymmddhh24miss"); // 获取系统当前时间
    strncpy(strddatetime+12,"00",2);        // 把数据时间中的秒固定填00

    // 2) 根据站点参数，生成站点观测数据（随机数）;
    crtsurfdata();

    // 3) 把容器datalist中的气象观测数据写入文件，outpath-数据文件存放的目录；datafmt-数据文件的格式，取值：csv、xml和json
    if(strstr(argv[4],"csv")!=0)    crtsurffile(argv[2],"csv");
    if(strstr(argv[4],"xml")!=0)    crtsurffile(argv[2],"xml");
    if(strstr(argv[4],"json")!=0)    crtsurffile(argv[2],"json");

    // sleep(10);  // 测试EXIT函数
    logfile.write("crtsurfdata 运行结束。\n");

    return 0;
}

// 程序退出和信号2、15的处理函数。
void EXIT(int sig){
    logfile.write("程序退出，sig=%d\n\n",sig);

    exit(0);
}

// 把站点参数文件加载到stlist容器中
bool loadstcode(const string &inifile){
    cifile ifile;   // 读取文件的对象
    if(ifile.open(inifile)==false){
        logfile.write("ifile.open(%s) failed.\n",inifile.c_str());
        return false;
    }

    string strbuffer;   // 存放从文件中读取的每一行

    ifile.readline(strbuffer);  // 读取站点参数文件的第一行，它是标题，扔掉

    ccmdstr cmdstr;             // 用于拆分从文件中读取的行
    st_stcode stcode;           // 站点参数的结构体
    
    while(ifile.readline(strbuffer)){
        // 测试是否读取到文件中的数据
        //logfile.write("strbuffer=%s\n",strbuffer.c_str());

        // 拆分从文件中读取的行，例如：安徽,58015,砀山,34.27,116.2,44.2
        cmdstr.splittocmd(strbuffer,",");   // 拆分字符串
        
        memset(&stcode,0,sizeof(st_stcode));

        cmdstr.getvalue(0,stcode.provname,30);  // 省
        cmdstr.getvalue(1,stcode.obtid,30);  // 站点号
        cmdstr.getvalue(2,stcode.obtname,30);  // 站名
        cmdstr.getvalue(3,stcode.lat);  // 纬度
        cmdstr.getvalue(4,stcode.lon);  // 经度
        cmdstr.getvalue(5,stcode.height);  // 海拔高度
        
        stlist.push_back(stcode);
    }

    return true;
}

// 根据stlist容器中的站点参数，生成站点观测数据，存放在datalist中
void crtsurfdata(){
    srand(time(0));     // 播随机数种子

    st_surfdata stsurfdata; // 观测数据的结构体

    // 遍历气象站点参数的stlist容器，生成每个站点的观测数据
    for(auto &aa:stlist){
        memset(&stsurfdata,0,sizeof(st_surfdata));

        // 模拟填充观测数据的结构体成员
        strcpy(stsurfdata.obtid,aa.obtid);  // 站点号
        strcpy(stsurfdata.ddatetime,strddatetime);  // 数据时间
        stsurfdata.t = rand()%350;                  // 气温：单位，摄氏度   0-350之间，可犯可不犯的错误不要犯
        stsurfdata.p = rand()%265+10000;            // 气压：单位，百帕
        stsurfdata.u = rand()%101;                  // 相对湿度：0-100之间的值
        stsurfdata.wd = rand()%360;                 // 风向：0-360之间的值
        stsurfdata.wf = rand()%150;                 // 风速：单位，m/s
        stsurfdata.r = rand()%16;                   // 降雨量：单位，mm
        stsurfdata.vis = rand()%5001+100000;        // 能见度，单位，m

        datalist.push_back(stsurfdata);             // 把观测数据的结构体放入datalist容器
    }

    // // 测试：是否模拟成功
    // for (auto &aa:datalist){
    //     logfile.write("%s,%s,%.1f,%.1f,%d,%d,%.1f,%.1f,%.1f\n",aa.obtid,aa.ddatetime,aa.t/10.0,aa.p/10.0,aa.u,aa.wd,aa.wf/10.0,aa.r/10.0,aa.vis/10.0);
    // }
    
}

// 把容器datalist中的气象观测数据写入文件，outpath-数据文件存放的目录；datafmt-数据文件的格式，取值：csv、xml和json
bool crtsurffile(const string& outpath,const string& datafmt){
    // 拼接生成数据的文件名，例如：/tmp/idc/surfdata/SURF_ZH_20210629092200_2254.csv
    string strfilename = outpath+"/"+"SURF_ZH_"+strddatetime+"_"+to_string(getpid())+"."+datafmt;

    cofile ofile;   // 写入数据文件的对象

    if(ofile.open(strfilename)==false){
        logfile.write("ofile.open(%s) failed.\n",strfilename.c_str());
        return false;
    }

    // 把datalist容器中的观测数据写入文件，支持csv、xml和json三种格式
    if(datafmt=="csv")  ofile.writeline("站点号,数据时间,气温,气压,相对湿度,风向,风速,降雨量,能见度\n");
    if(datafmt=="xml")  ofile.writeline("<data>\n");
    if(datafmt=="json")  ofile.writeline("{\"data\":[\n");
    for(auto &aa:datalist){
        if(datafmt=="csv")  ofile.writeline("%s,%s,%.1f,%.1f,%d,%d,%.1f,%.1f,%.1f\n",\
                                            aa.obtid,aa.ddatetime,aa.t/10.0,aa.p/10.0,aa.u,aa.wd,aa.wf/10.0,aa.r/10.0,aa.vis/10.0);
        if(datafmt=="xml")  ofile.writeline("<obtid>%s</obtid><ddatetime>%s</ddatetime><t>%.1f</t><p>%.1f</p>"\
                                            "<u>%d</u><wd>%d</wd><wf>%.1f</wf><r>%.1f</r><vis>%.1f</vis><endl/>\n",\
                                            aa.obtid,aa.ddatetime,aa.t/10.0,aa.p/10.0,aa.u,aa.wd,aa.wf/10.0,aa.r/10.0,aa.vis/10.0);
        if(datafmt=="json"){
            ofile.writeline("{\"data\":\"%s\",\"ddatetime\":\"%s\",\"t\":\"%.1f\",\"p\":\"%.1f\",\"u\":\"%d\","\
                            "\"wd\":\"%d\",\"wf\":\"%.1f\",\"r\":\"%.1f\",\"vis\":\"%.1f\"}",\
                            aa.obtid,aa.ddatetime,aa.t/10.0,aa.p/10.0,aa.u,aa.wd,aa.wf/10.0,aa.r/10.0,aa.vis/10.0);
            // 注意，json文件的最后一条记录不需要逗号，用一下代码特殊处理
            static int ii = 0;
            if(ii<datalist.size()-1){
                // 如果不是最后一行
                ofile.writeline(",\n");ii++;
            }
            else{
                ofile.writeline("\n");
            }
        }  
    }

    if(datafmt=="xml")  ofile.writeline("</data>\n");
    if(datafmt=="json")  ofile.writeline("]}\n");

    ofile.closeandrename(); // 关闭临时文件，并改名为正式的文件

    logfile.write("生成数据文件%s成功，数据时间%s，记录数%d。\n",strfilename.c_str(),strddatetime,datalist.size());

    return true;
}